package com.pdg.mcplugin.tsa;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashSet;
import java.util.logging.Logger;

import org.bukkit.Location;
import org.bukkit.Material;

public class DataProvider {
	//plugin
	private TSA plugin;
	public TSA getPlugin(){
		return plugin;
	}
	
	//logger
	public Logger getLogger(){
		return getPlugin().getLogger();
	}
	
	private void ensureDataFolder(){
		File folder = getPlugin().getDataFolder();
		if(!folder.exists()){
			if(folder.mkdir()){
				getLogger().info(Constants.messageCreatedDataFolder);
			}else{
				getLogger().severe(Constants.errorCouldNotCreateDataFolder);
			}
		}
	}
	
	//connection
	private Connection connection;
	public Connection getConnection(){
		if(connection==null){
			ensureDataFolder();
			try{
				Class.forName(Constants.classNameSQLite);
			} catch(Exception e){
				getLogger().info(Constants.errorCouldNotLoadSQLite);
				e.printStackTrace();
				return null;
			}
			try{
				connection = DriverManager.getConnection(String.format(Constants.connectionString,getPlugin().getDataFolder().toString()));
			}catch(Exception e){
				getLogger().info(Constants.errorCouldNotConnectToDatabase);
				e.printStackTrace();
				connection = null;
				return null;
			}
		}
		return connection;
	}
	
	private void ensureArrivalsTable(){
		try{
			Statement statement = getConnection().createStatement();
			ResultSet result = statement.executeQuery(Constants.dbCheckForArrivalsTable);
			if(!result.next()){
				statement = getConnection().createStatement();
				statement.execute(Constants.dbCreateArrivalsTable);
			}
		}catch(Exception e){
			getLogger().info(Constants.errorCouldNotCreateArrivalsTable);
			e.printStackTrace();
		}
	}
	
	private void ensureContrabandTable(){
		try{
			Statement statement = getConnection().createStatement();
			ResultSet result = statement.executeQuery(Constants.dbCheckForContrabandTable);
			if(!result.next()){
				statement = getConnection().createStatement();
				statement.execute(Constants.dbCreateContrabandTable);
				
				for(Material material:Constants.defaultContraband){
					prohibitContraband(material);
				}
			}
		}catch(Exception e){
			getLogger().info(Constants.errorCouldNotCreateContrabandTable);
			e.printStackTrace();
		}
	}
	
	private void ensureBlocksTable(){
		try{
			Statement statement = getConnection().createStatement();
			ResultSet result = statement.executeQuery(Constants.dbCheckForBlocksTable);
			if(!result.next()){
				statement = getConnection().createStatement();
				statement.execute(Constants.dbCreateBlocksTable);
			}
		}catch(Exception e){
			getLogger().info(Constants.errorCouldNotCreateBlocksTable);
			e.printStackTrace();
		}
	}
	
	private String sqlSafeName(String theName){
		return theName.replaceAll("'","''");
	}
	
	//create an arrival point
	public boolean createArrival(String arrivalName,Location arrivalLocation){
		ensureArrivalsTable();
		if(retrieveArrival(arrivalName)!=null) return false;
		try{
			Statement statement = getConnection().createStatement();
			statement.execute(String.format(Constants.dbInsertArrivalPoint,sqlSafeName(arrivalName), sqlSafeName(arrivalLocation.getWorld().getName()),arrivalLocation.getX(),arrivalLocation.getY(),arrivalLocation.getZ()));
			return true;
		}catch(Exception e){
			getLogger().severe(Constants.errorCouldNotCreateArrivalPoint);
			e.printStackTrace();
		}
		return false;
	}
	
	//retrieve an arrival point
	public Location retrieveArrival(String arrivalName){
		ensureArrivalsTable();
		try{
			Statement statement = getConnection().createStatement();
			ResultSet result = statement.executeQuery(String.format(Constants.dbRetrieveArrivalPoint,sqlSafeName(arrivalName)));
			if(result.next()){
				return new Location(
						getPlugin().getServer().getWorld(result.getString(Constants.fieldArrivalWorld)),
						result.getDouble(Constants.fieldArrivalX),
						result.getDouble(Constants.fieldArrivalY),
						result.getDouble(Constants.fieldArrivalZ));
			}
		}catch(Exception e){
			getLogger().severe(Constants.errorCouldNotRetrieveArrivalPoint);
			e.printStackTrace();
			return null;
		}
		return null;
	}
	
	//delete an arrival point
	public boolean deleteArrival(String arrivalName){
		ensureArrivalsTable();
		if(retrieveArrival(arrivalName)==null) return false;
		try{
			Statement statement = getConnection().createStatement();
			statement.execute(String.format(Constants.dbDeleteArrivalPoint,arrivalName));
			return true;
		}catch(Exception e){
			getLogger().severe(Constants.errorCouldNotDeleteArrivalPoint);
			e.printStackTrace();
			return false;
		}
	}
	
	public boolean permitContraband(Material theMaterial){
		ensureContrabandTable();
		if(!checkContraband(theMaterial)){
			return true;
		}else{
			try{
				Statement statement = getConnection().createStatement();
				statement.execute(String.format(Constants.dbPermitContraband,theMaterial.getId()));
				return true;
			}catch(Exception e){
				getLogger().severe(Constants.errorCouldNotPermitContraband);
				e.printStackTrace();
				return false;
			}
		}
	}
	
	public boolean prohibitContraband(Material theMaterial){
		ensureContrabandTable();
		if(checkContraband(theMaterial)){
			return true;
		}else{
			try{
				Statement statement = getConnection().createStatement();
				statement.execute(String.format(Constants.dbProhibitContraband,theMaterial.getId()));
				return true;
			}catch(Exception e){
				getLogger().severe(Constants.errorCouldNotProhibitContraband);
				e.printStackTrace();
				return false;
			}
		}
	}
	
	public boolean checkContraband(Material theMaterial){
		ensureContrabandTable();
		try{
			Statement statement=getConnection().createStatement();
			ResultSet result = statement.executeQuery(String.format(Constants.dbCheckContraband, theMaterial.getId()));
			if(result.next()){
				return true;
			}else{
				return false;
			}
		}catch(Exception e){
			e.printStackTrace();
			return false;
		}
	}
	
	public void clearContraband(){
		ensureContrabandTable();
		try{
		Statement statement = getConnection().createStatement();
		statement.execute(Constants.dbClearContraband);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public HashSet<Material> listContraband(){
		ensureContrabandTable();
		try{
			Statement statement = getConnection().createStatement();
			ResultSet result = statement.executeQuery(String.format(Constants.dbListContraband));
			HashSet<Material> materials = new HashSet<Material>();
			while(result.next()){
				Material material = Material.getMaterial(result.getInt(Constants.fieldMaterialId));
				materials.add(material);
			}
			return materials;
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}
	
	public boolean setBlockProtection(Location theLocation,Integer theProtectionLevel){
		ensureBlocksTable();
		try{
			if(theProtectionLevel<=Constants.protectionZero){
				Statement statement = getConnection().createStatement();
				statement.execute(String.format(Constants.dbDeleteBlock,theLocation.getWorld().getName(),theLocation.getX(),theLocation.getY(),theLocation.getZ()));
				return true;
			}else if(theProtectionLevel>Constants.protectionMaximum){
				theProtectionLevel = Constants.protectionMaximum;
			}
			Statement statement = getConnection().createStatement();
			statement.execute(String.format(Constants.dbUpdateBlock,theProtectionLevel,theLocation.getWorld().getName(),theLocation.getX(),theLocation.getY(),theLocation.getZ()));
			if(statement.getUpdateCount()==1) return true;
			statement = getConnection().createStatement();
			statement.execute(String.format(Constants.dbCreateBlock,theLocation.getWorld().getName(),theLocation.getX(),theLocation.getY(),theLocation.getZ(),theProtectionLevel));
			return true;
		}catch(Exception e){
			e.printStackTrace();
			return false;
		}
	}
	
	public Integer getBlockProtection(Location theLocation){
		ensureBlocksTable();
		try{
			Statement statement = getConnection().createStatement();
			ResultSet result = statement.executeQuery(String.format(Constants.dbRetrieveBlock,theLocation.getWorld().getName(),theLocation.getX(),theLocation.getY(),theLocation.getZ()));
			if(result.next()){
				return result.getInt(Constants.fieldBlockProtection);
			}else{
				return Constants.protectionZero;
			}
		}catch(Exception e){
			e.printStackTrace();
			return Constants.protectionZero;
		}
	}
	
	public boolean incrementBlockProtection(Location theLocation){
		return setBlockProtection(theLocation,getBlockProtection(theLocation)+1);
	}
	
	public boolean decrementBlockProtection(Location theLocation){
		return setBlockProtection(theLocation,getBlockProtection(theLocation)-1);
	}
	
	//constructor
	public DataProvider(TSA thePlugin){
		plugin = thePlugin;
	}
}
